#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <stdlib.h>
#include <string.h>

extern char** environ;

// 验证下面所说的不是真实的物理地址而是虚拟地址
// 父子进程同时打印同一个变量，变量地址一样，但值却不一样，如果这是真正的内存的话
// 是不可能做到的，所以证明，这就不是内存，而是虚拟空间地址
int val = 100;

int main()
{
    if(fork() == 0)
    {
        // child
        while(1)
        {
            printf("我是子进程，val = %d, &val = %p\n",val,&val);
            val+=100;
            sleep(1);
        }
    }
    else
    {
        // father
        
        while(1)
        {
            printf("我是父进程，val = %d, &val = %p\n",val,&val);
            sleep(1);
        }
    }
    return 0;
}

// 进程地址空间/虚拟地址空间
// C/C++了解到的栈、堆、静态区、常量区等都不是真正的物理地址，而是虚拟地址
// 栈区向下增长，堆区向上增长
// 堆区和栈区中间有很大一段镂空的空间
int g_unval;
int g_val = 100;
int main3(int argc, char *argv[], char *env[])
{
     const char *str = "helloworld";
     printf("code addr: %p\n", main);
     printf("init global addr: %p\n", &g_val);
     printf("uninit global addr: %p\n", &g_unval);
     static int test = 10;
 char *heap_mem = (char*)malloc(10);
 char *heap_mem1 = (char*)malloc(10);
 char *heap_mem2 = (char*)malloc(10);
 char *heap_mem3 = (char*)malloc(10);
    printf("heap addr: %p\n", heap_mem); //heap_mem(0), &heap_mem(1)
 printf("heap addr: %p\n", heap_mem1); //heap_mem(0), &heap_mem(1)
 printf("heap addr: %p\n", heap_mem2); //heap_mem(0), &heap_mem(1)
 printf("heap addr: %p\n", heap_mem3); //heap_mem(0), &heap_mem(1)
 printf("test static addr: %p\n", &test); //heap_mem(0), &heap_mem(1)
 printf("stack addr: %p\n", &heap_mem); //heap_mem(0), &heap_mem(1)
 printf("stack addr: %p\n", &heap_mem1); //heap_mem(0), &heap_mem(1)
 printf("stack addr: %p\n", &heap_mem2); //heap_mem(0), &heap_mem(1)
 printf("stack addr: %p\n", &heap_mem3); //heap_mem(0), &heap_mem(1)
 printf("read only string addr: %p\n", str);
 for(int i = 0 ;i < argc; i++)
 {
 printf("argv[%d]: %p\n", i, argv[i]);
 }
 for(int i = 0; env[i]; i++)
 {
 printf("env[%d]: %p\n", i, env[i]);
 }
 return 0;
}


void learnEnvirChara()
{
    // 理解环境变量的特点
    // 环境变量具有全局特性
    // 本地变量—bash除了创建环境变量列表和命令行参数表还有本地变量列表
    // 可以用set查看本地变量表
    // i=10，这样可以直接增加一个本地变量i
    // 还可以通过exprot i 来增加本地变量
    // 本地变量的存在是为了适应shell脚本语言，让shell可以不只是命令行，也可以定义变量这类的语句
    // 我们的环境变量都存在bash中
    // 向pwd和export这样的命令叫做内建命令—built_in命令，它不需要通过子进程来执行，而是直接由bash执行，bash通过调用函数/系统调用来实现，计算这种命令没有在PATH中，也可以直接使用
    // 本地变量不会被子进程继承，本地变量只在bash内部使用
}

void getEnvironment()
{
    // 我们可以借助main函数的第三个参数env来遍历环境变量列表
    // 环境变量可以被子进程继承
    
    // 我们可以借助getenv()来获取某个环境变量
    // 我们可以借助这个getenv来实现很多需要分类执行的程序
    // char* e =  getenv("PATH");
    // if(e == NULL) return;
    // printf("%s\n",e);
    if(strcmp(getenv("USER"), "xsc") == 0)
    {
        printf("我是xsc,只有xsc才可以执行该程序\n");
    }
    else
    {
        printf("user error\n");
    }

    // 借助extern char** environ来遍历环境变量列表
    for(int i=0; environ[i]; ++i)
    {
        printf("environ[%d]->%s\n",i,environ[i]);
    }
}


void otherEnvironment()
{
    // 我们可以借助env命令，查看当前的环境变量，这下面有几个常见的
    // PATH:系统搜素指令的默认搜索路径
    // HOME:当前用户的家目录
    // PWD:当前的工作目录
    // USER:当前用户
    // LOGNAME:当前登录的用户
}

void environment_PATH()
{
    // 但是大家有没有注意到，为什么我们写的程序前面需要带./cmd
    // 而ls、cd这样的命令不需要呢？
    // 首先，系统执行我们的程序，首先要知道我们的二进制程序在哪，而我们所写的程序就在当前目录下，所以要使用./cmd来表示
    // 而这些系统命令，之所以不需要，是因为环境变量PATH的存在，PATH里面包含了一些绝对路径,我们可以使用env查看所有的环境变量，也可以echo $xxx
    // echo $PATH
    // /usr/local/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/home/xsc/.local/bin:/home/xsc/bin
    // 我们系统的命令都在/usr/bin/下，而PATH里面就包含了该路径
    // 当我们使用一个命令没有带路径时，机会从PATH中寻找，如果没找到就会报错
    // 所以PATH环境变量就是系统中搜索指令的默认搜索路径
    
    // 当我们一登陆，此时bash就会从配置文件中加载出一个环境变量表，这个表是配置文件的一份拷贝
    // 所以我们直接修改这个表的话是一种暂时的修改，当我们重启终端，该修改就消失了
    // export PATH=$PATH:/home/xsc/linux/lesson_2_12,我们可以借助这条命令加入到PATH环境变量中，之后我们就可以直接test，而不用./test了
    // 我们也可以直接将test拷贝到/usr/bin目录下，同样我们也可以直接使用test执行，其实该目录就包含在PATH中
    
    // bash除了拿到环境变量表，还会拿到命令行参数表
    // 因为bash要对命令行进行拆分，然会将命令在PATH路径中进行寻找，同时匹配选项
    while(1)
    {
        printf("test:我是bash的子进程，pid:%d,ppid:%d\n",getpid(),getppid());
        sleep(1);
    }

    // 如果想要永久的修改PATH,我们就得修改配置文件，在用户的家目录下有两个隐藏文件
    // .bash_profile 和 .bashrc
    // 当登录用户之后，.bash_profile文件会调用.bashrc文件，.bashrc文件则会调用/etc/bashrc文件，然后加载我们的配置文件，生成环境变量表
    // 我们修改.bash_profile文件里面的PATH就可达到永久修改PATH的效果
}

int main2(int argc, char* argv[], char* env[])
{

    getEnvironment();

    (void)argc;
    (void)argv;
    (void)env;
//    environment_PATH();
//   
//   
//    (void)argc;
//    (void)argv;
//    for(int i=0; env[i]; i++)
//    {
//        printf("env[%d]->%s\n",i,env[i]);
//    }
//
    return 0;
}


// 命令行参数
// main函数有三个参数，分别是参数个数，参数表，环境变量表
// 我们在命令行上输入的命令都是一个一个的参数，bash会将这些参数以空格进行划分，传给argv参数表
// 命令行参数的存在可以实现不同的功能，这也是linux指令选型的实现原理
// linux指令其实也是用c/c++写的，它们也有main函数，所以我们可以传不同的命令行参数来实现不同的功能
// 这也就从侧面反映出了，程序的入口并不是main函数，而是一个_start_函数
int main1(int argc, char* argv[],char* env[])
{
    (void)argc;
    (void)argv;
    (void)env;
    if(argc == 0)
    {
        printf("错误使用，cmd [-a|-b|-c...]\n");
    }
    else if(argc == 1)
    {
        printf("默认用法\n");

    }
    else if(argc == 2)
    {
        printf("使用方法1\n");
    }
    else if(argc == 3)
    {
        printf("使用方法2\n");
    }
    else
    {
        printf("错误使用，cmd [-a|-b|-c...]\n");
    }

    return 0;
}
